<template>
    <div class="flow-container">
        <div id="flow-stencil" :style="{ display: isView ? 'none' : '' }">
            <el-input style="width: 95%;margin:2.5%"
                      v-model="searchText"
                      placeholder="请输入搜索内容"
                      class="flow-search-input"
                      prefix-icon="Search"
                      clearable
                      @keyup.enter="search"
            />
          <el-scrollbar style="height:calc(100% - 60px);">
            <el-menu
              :default-openeds="defaultOpeneds"
              unique-opened
              mode="vertical"
              class="flow-stencil-menu"
              @open="handleOpen"
              @close="handleClose"
            >
              <template v-for="(item, index) in stencilList" :key="item.title">
                <!-- 可折叠的父菜单 -->
                <el-sub-menu :index="index.toString()">
                  <template #title>
                    <div class="flow-stencil-title">{{ item.title }}</div>
                  </template>

                  <!-- 子菜单 -->
                  <template v-for="(ct, ctIndex) in item.data">
                    <el-menu-item
                      v-if="!ct.hide"
                      :key="ctIndex"
                      :index="`${index}-${ctIndex}`"
                      @mousedown.stop="initStencil(ct, $event)"
                    >
                      <img :src="ct.img" class="flow-stencil-icon" alt=""/>
                      <span>{{ ct.label }}</span>
                    </el-menu-item>
                  </template>
                </el-sub-menu>
              </template>
            </el-menu>
          </el-scrollbar>


        </div>
        <div id="flow-main"></div>
        <div id="flow-toolbar">
            <div class="flow-toolbar-item">
                <span>
                    流程名称：
                    <el-input v-model="flowName" @change="changeName" placeholder="请输入流程名称"
                              style="width: 150px;margin-right:10px;"/>
                </span>
                <!--                <el-button type="primary" icon="CaretRight" @click="toJSON" circle />-->
                <el-button icon="CaretRight" @click="toJSON">保存</el-button>
                <el-button v-if="currentRoute.query.type !== '2'" icon="RefreshLeft" @click="toRun">运行</el-button>

            </div>
            <div class="flow-toolbar-item">
                <el-button icon="Close" @click="toClose">关闭</el-button>
            </div>
        </div>
    </div>


    <drag-dialog v-if="detailVisible" v-model="detailVisible" width="1000px" title="属性配置">
        <component :is="getComponent"
                   @cancel="closeDetailVisible"
                   @getComponentId="getComponentId"
                   :detailObject="detailObject"
                   @update-data="handleDataUpdate"></component>
    </drag-dialog>


    <drag-dialog v-if="logVisible" v-model="logVisible" width="800px" title="运行调试">
        <log-dialog @cancel="logVisible=false" @toRun="toRunStart"/>
    </drag-dialog>


    <drag-dialog v-if="runVisible" v-model="runVisible" fullscreen title="运行">
        <run-dialog/>
    </drag-dialog>


</template>
<script setup>
import {ref, onMounted, computed, nextTick, watch} from "vue";
import {flowServiceStore} from '@data/store/flow';
import {intServiceStore} from '@data/store/index';

import {storeToRefs} from 'pinia';
import {Graph, Shape} from "@antv/x6";
import {Snapline} from "@antv/x6-plugin-snapline";
import {Selection} from "@antv/x6-plugin-selection";
import {Keyboard} from "@antv/x6-plugin-keyboard";
import {Clipboard} from "@antv/x6-plugin-clipboard";
import {History} from "@antv/x6-plugin-history";
import {register} from '@antv/x6-vue-shape'
import CustomNode from './components/CustomNode.vue'
import {Dnd} from '@antv/x6-plugin-dnd'
import logDialog from './disposition/log.vue'
import runDialog from './run.vue'
import {searchAll, insertV2, updateByIdV2, selectById} from "@data/api/data/dataDevelopment";
import {ElMessage, ElMessageBox} from 'element-plus'
import {useRoute} from 'vue-router'

const currentRoute = useRoute();

import EdgeConfig from "@/platform/data/views/data/dataDevelopment/components/EdgeConfig.vue";

import {deepClone} from "@/utils";
import {Base64} from "js-base64";


const activeName = ref('first')

const logVisible = ref(false);
const detailVisible = ref(false);
const runVisible = ref(false);
const detailObject = ref({});

const componentId = ref(''); // 操作的组件id
const defaultOpeneds = ref([]); // 默认展开项

// 链接桩样式
const portStyle = {
    r: 4,
    magnet: true,
    stroke: "#5F95FF",
    strokeWidth: 1,
    fill: "#fff",
    style: {
        visibility: "hidden",
    },
}

// 链接桩配置
const ports = {
    // 设置链接桩分组
    groups: {
        top: {
            // 定义连接柱的位置，如果不配置，将显示为默认样式
            position: 'top',
            // 定义连接柱的样式
            attrs: {
                circle: {
                    ...portStyle
                }
            }
        },
        right: {
            position: 'right',
            attrs: {
                circle: {
                    ...portStyle
                }
            }
        },
        bottom: {
            position: 'bottom',
            attrs: {
                circle: {
                    ...portStyle
                }
            }
        },
        left: {
            position: 'left',
            attrs: {
                circle: {
                    ...portStyle
                }
            }
        },
        // absolute: {
        //     position: 'absolute',
        //     attrs: {
        //         circle: {
        //             r: 6,
        //             magnet: true,
        //             stroke: '#008CFF',
        //             strokeWidth: 2,
        //             fill: '#fff'
        //         }
        //     }
        // }
    },
    // 链接桩
    items: [
        {
            group: 'top'
        },
        {
            group: 'right'
        },
        {
            group: 'bottom'
        },
        {
            group: 'left'
        }
    ]
}

const flowCount = flowServiceStore();
const intCount = intServiceStore();
const {graph, stencil, stencilList, flowData} = storeToRefs(flowCount);
const {flowName} = storeToRefs(intCount);

watch(
  () => stencilList.value,
  (newList) => {
    if (newList.length > 0) {
      defaultOpeneds.value = ["0"];
    }
  },
  { immediate: true }
);

watch(
  () => flowCount.flowData,
  (newFlowData) => {
    if (!graph.value) return;
    graph.value.getNodes().forEach(node => {
      const nodeId = node.id;
      const nodeData = newFlowData[nodeId];
      if (nodeData && nodeData !== node.getData()) {
        node.setData(nodeData);
        node.attr('text/text', nodeData.name || nodeData.label);
        // node.update();
      }
    });
  },
  { deep: true }
);

let isView = ref(false);
onMounted(async () => {
  await initStencilList(() => {
        init();
        // 注册插件
        initGraphUse();
        // 注册自定义节点
        registerNode();
        // initStencil();
        // loadStencilNode();
        initEvent();
        initData();
    });


})


let initStencilList = async (callback) => {
    const res = await searchAll();
    // stencilList.value = res;
    let arr = deepClone(stencilList.value);
    arr.map((item, index) => {
        if (index != 0)
            item.data = []
    })
    res.data.map(item => {
        if(item.visible === 'Y') {
            arr[item.pluginCategory * 1].data.push({
                name: item.pluginType,
                label: item.pluginName,
                component: item.pluginType,
                img: new URL('@/assets/imgs/icon-start.png', import.meta.url).href,
            })
        }
    })
    console.log(arr)
    flowCount.setStencilList(arr);
    if (callback) callback();
}


let searchText = ref('');
let search = () => {
    stencilList.value.map(item => {
        item.data.map(item => {
            item.hide = !item.label.includes(searchText.value)
        })
    })

}


let changeName = (val) => {
    intCount.setFlowName(val);
}

let init = () => {
    graph.value = new Graph({
        container: document.getElementById("flow-main"),
        autoGenerateId: false, // 禁用自动ID生成
        grid: {
            visible: false,
            size: 20, // 网格大小
            type: 'mesh',
            args: {
                color: '#e9e9e9',
                thickness: 2 // 网格线宽度/网格点大小
            }
        },
        // 滚轮缩放 MouseWheel
        mousewheel: {
            enabled: true,
            zoomAtMousePosition: true,
            modifiers: ['ctrl', 'meta'],
            maxScale: 3,
            minScale: 0.3
        },
        // 连线规则
        connecting: {

            // 路由类型
            // router: 'orth',
            // 圆角连接器，将起点、路由点、终点通过直线按顺序连接，并在线段连接处通过圆弧连接（倒圆角）。
            connector: {
                name: 'smooth',
            },
            anchor: 'center', // 当连接到节点时，通过 anchor 来指定被连接的节点的锚点，默认值为 center
            connectionPoint: 'anchor', // 指定连接点，默认值为 boundary
            // 是否允许连接到画布空白位置的点，默认为 true。
            allowBlank: false,
            allowLoop: false,
            allowEdge: true, // 允许在连线上拖拽调整
            allowMulti: false,
            validateConnection: ({ sourceCell, targetCell, targetMagnet }) => {
                return sourceCell !== targetCell && !!targetMagnet;
            },
            // 距离节点或者连接桩 20px 时会触发自动吸附
            snap: {
                radius: 40,
                effect: 'stroke' // 吸附时显示高亮
            },
            // 拽出新的边
            createEdge() {
              const edgeId = flowServiceStore().generateNextEdgeId();

                return new Shape.Edge({
                    id: edgeId,
                    attrs: {
                        line: {
                            stroke: "#A2B1C3",
                            strokeWidth: 2,
                            // targetMarker: 'block',
                            targetMarker: {
                              name: 'block',
                              width: 8,
                              height: 6
                            },
                        },
                    },
                    // 可拖拽箭头工具
                    tools: [{
                      name: 'target-arrowhead',
                      args: {
                        attrs: {
                          d: 'M -8 -4 0 0 -8 4 Z',
                          width: 8,
                          height: 6,
                          fill: '#A2B1C3',       // 工具颜色
                          stroke: '#A2B1C3',
                          offset: -2         // 调整工具位置
                        }
                      }
                    }],
                    zIndex: 0,
                    data: {
                        type: 'edge', // 明确标识类型
                        distribute: 'Y',
                        sourceNodeId: '',
                        targetNodeId: ''
                    }
                })
            },
            // validateConnection({targetMagnet}) {
            //     return !!targetMagnet
            // }
        },
        // 连线高亮
        highlighting: {
            // 连线过程中，自动吸附到链接桩时被使用。
            magnetAdsorbed: {
                name: 'stroke',
                args: {
                    attrs: {
                        width: 12,
                        r: 6,
                        magnet: true,
                        stroke: '#008CFF',
                        strokeWidth: 2,
                        fill: '#0F67FF'
                    }
                }
            }
        },
        resizing: {
            minWidth: 100,
            minHeight: 30,
        },
        snapline: true,
        keyboard: {
            enabled: true,
        },
        clipboard: {
            enabled: true,
        },
        history: {
            enabled: true,
        },
    });
}

let initGraphUse = () => {
    graph.value
        .use(
            new Selection({
                enabled: true,
                rubberband: true,
                // showNodeSelectionBox: true,
            })
        )
        .use(
            new Snapline({
                enabled: true,
            })
        )
        .use(
            new Keyboard({
                enabled: true,
            })
        )
        .use(
            new Clipboard({
                enabled: true,
            })
        )
        .use(
            new History({
                enabled: true,
            })
        );
};

let registerNode = () => {
    stencilList.value.map((e) => {
        e.data.map((item) => {
            let width = item.label != '开始' && item.label != '结束' ? 170 : 40;
            let height = item.label != '开始' && item.label != '结束' ? 65 : 40;
            register({
                shape: item.name,
                component: CustomNode,
                width: width,
                height: height,
                data: {
                    label: item.label,
                    shape: item.name,
                },
                attrs: {
                  body: {
                    'style': 'min-height: auto'
                  },
                },
                // port默认不可见
                ports: {...ports},
            })
        })
    })

}


let initStencil = (item, e) => {
  const nodeId = flowCount.generateNextId();

  const node = graph.value.createNode({
    id: nodeId,
    shape: item.name,
    x: 200,
    y: 300,
    ports: { ...ports },
    data: {
      label: item.label,
      component: item.component,
      id: nodeId
    }
  });

  const dnd = new Dnd({
    target: graph.value,
    getDragNode: (node) => node.clone({ keepId: true }), // 保持源节点id
    getDropNode: (node) => node.clone({ keepId: true }), // 保持源节点id
    validateNode: () => true
  });
  dnd.start(node, e);
}

const showPorts = (ports, show) => {
    for (let i = 0, len = ports.length; i < len; i = i + 1) {
        ports[i].style.visibility = show ? 'visible' : 'hidden'
    }
}


const closeDetailVisible = () => {
    detailVisible.value = false;
}

const getComponent = computed(() => {
    if(detailObject.value.data.type === 'edge') {
        return EdgeConfig
    }
    return detailObject.value.data.component
})

const getComponentId = (value) => {
    componentId.value = value;
}

const handleDataUpdate = (payload) => {
  const node = graph.value.getCellById(payload.id);
  if (node) {
    node.setData({
        ...node.getData(),
        ...payload,
        label: node.getData().label
    });
    node.attr('text/text', payload.name || node.getData().label);
    // node.updateData();
    console.log('更新后的node',node)
  }
  else if (payload.type === 'edge') {
    const edge = graph.value.getCellById(payload.id);
    if (edge) {
      // 更新边数据
      edge.setData(payload.data);

      // 可视化样式更新
      edge.attr('line/stroke', payload.data.color || '#A2B1C3');
      edge.setLabels([{
        attrs: {
          text: {
            text: payload.data.remark || '',
            fill: '#666'
          }
        }
      }]);
    }
  }
};

const hanldeNodeDelete = (node) => {
    const obj = deepClone(flowData.value);
    delete obj[node.id];
    flowCount.setFlowData(obj)
}

let initEvent = () => {
    graph.value.on('edge:click', ({ edge }) => {
      console.log('edge',edge)
        detailObject.value = {
            id: edge.id || edge.data.id,
            type: 'edge',
            data: JSON.parse(JSON.stringify(edge.getData())),
            sourceNodeId: edge.source?.cell,
            targetNodeId: edge.target?.cell
        };
        console.log('[DEBUG] 边点击数据:', detailObject.value);
        detailVisible.value = true;
    })

    graph.value.on("cell:click", ({e, cell}) => {

    });

    graph.value.on('custom:event', ({data}) => {
        if (data) {
          console.log('datatatat',data)
            // data.id = data.data.id
            detailObject.value = data;
            detailVisible.value = true;
        }
    });


    graph.value.on("edge:connected", ({isNew, edge}) => {
        if (isNew) {
            handleEdgeConnected(edge);
        }

    });


    graph.value.on('node:mouseenter', () => {
        const container = document.getElementById('flow-main');
        const ports = container.querySelectorAll(
            '.x6-port-body',
        )
        showPorts(ports, true)
    })
    graph.value.on('node:mouseleave', () => {
        const container = document.getElementById('flow-main');
        const ports = container.querySelectorAll(
            '.x6-port-body',
        )
        showPorts(ports, false)
    })
    // 删除节点触发
    graph.value.on("node:removed", ({node}) => {
        // 删除选中的元素
        const cells = graph.value.getSelectedCells();
        if (cells.length) {
            cells.forEach((cell) => {
                hanldeNodeDelete(cell);
            });
            graph.value.removeCells(cells);
        }
        flowCount.recycleNodeId(node.id);
    });
    graph.value.on("selection:changed", (args) => {
        args.added.forEach((cell) => {
            // this.hanldeCellClick(cell);
            const notDelete = ['custom-endpoint', 'custom-start', 'custom-end']
            if (!notDelete.some(n => n === cell.shape)) {
                cell.addTools([
                    {
                        name: "button-remove", // 工具名称
                    },
                ]);
            }
            if (cell.isEdge()) {
                cell.addTools([
                    {
                        name: "vertices",
                        args: {
                            padding: 4,
                            attrs: {
                                strokeWidth: 0.1,
                                stroke: "#2d8cf0",
                                fill: "#ffffff",
                            },
                        },
                    },
                ]);
            }
        });
        args.removed.forEach((cell) => {
            cell.removeTools();
        });
    });
    // 连接桩点击
    graph.value.on("node:port:mousedown", (event) => {
        console.log("node:port:mousedown", event);
    });
    graph.value.on("node:port:mouseup", (event) => {
        console.log("node:port:mouseup", event);
    });

    // 点击画布空白区域
    graph.value.on("blank:click", () => {
        graph.value.getCells().forEach(cell => {
          if (cell.isNode()) {  // 只移除节点工具，保留边工具
            cell.removeTools()
          }
        })
        graph.value.cleanSelection && graph.value.cleanSelection();
    });

    // copy cut paste
    graph.value.bindKey(["meta+c", "ctrl+c"], () => {
        const cells = graph.value.getSelectedCells();
        if (cells.length) {
            graph.value.copy(cells);
        }
        return false;
    });

    graph.value.bindKey(["meta+x", "ctrl+x"], () => {
        const cells = graph.value.getSelectedCells();
        if (cells.length) {
            graph.value.cut(cells);
        }
        return false;
    });
    graph.value.bindKey(["meta+v", "ctrl+v"], () => {
        if (!graph.value.isClipboardEmpty()) {
            const cells = graph.value.paste({offset: 32});
            graph.value.cleanSelection();
            graph.value.select(cells);
        }
        return false;
    });

    // undo redo
    graph.value.bindKey(["meta+z", "ctrl+z"], () => {
        if (graph.value.canUndo()) {
            graph.value.undo();
        }
        return false;
    });
    graph.value.bindKey(["meta+shift+z", "ctrl+shift+z"], () => {
        if (graph.value.canRedo()) {
            graph.value.redo();
        }
        return false;
    });

    // select all
    graph.value.bindKey(["meta+shift+a", "ctrl+shift+a"], () => {
        const nodes = graph.value.getNodes();
        if (nodes) {
            graph.value.select(nodes);
        }
    });

    // delete
    graph.value.bindKey(["backspace", "delete"], () => {
        console.log("backspace");
        // 删除选中的元素
        const cells = graph.value.getSelectedCells();
        if (cells.length) {
            cells.forEach((cell) => {
                hanldeNodeDelete(cell);
            });
            graph.value.removeCells(cells);
        }
        // this.setCurrentShape("custom-start");
    });

    // zoom
    graph.value.bindKey(["ctrl+1", "meta+1"], () => {
        const zoom = graph.value.zoom();
        if (zoom < 1.5) {
            graph.value.zoom(0.1);
        }
    });
    graph.value.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = graph.value.zoom();
        if (zoom > 0.5) {
            graph.value.zoom(-0.1);
        }
    });
  // 监听边删除
  graph.value.on('edge:removed', ({ edge }) => {
    flowCount.recycleEdgeId(edge.id);
    const newData = { ...flowCount.flowData };
    delete newData[edge.id];
    flowCount.setFlowData(newData);
  });
}


let handleEdgeConnected = (edge) => {

    // console.log(edge);
    // console.log(graph.value.toJSON())
  const sourceId = edge.getSourceCell().id;
  const targetId = edge.getTargetCell().id;

  // 更新边数据
  edge.setData({
    ...edge.getData(),
    sourceNodeId: sourceId,
    targetNodeId: targetId
  });

  flowCount.setFlowData({
    ...flowCount.flowData,
    [edge.id]: edge.getData()
  });
}


let setData = () => {

    let arr = graph.value.toJSON().cells.filter(item => item.shape != "edge" && item.shape != 'start-node' && item.shape != 'end-node');
    arr = arr.map(element => {
        return {
            // ...element,
            name: element.data.label,
            type: element.data.component,
            id: element.id,
            ...flowData.value[element.id]
        }
    })
    let lineArr = graph.value.toJSON().cells.filter(item =>
        item.shape == "edge" &&
        graph.value.toJSON().cells.find(e => e.id == item.source.cell).shape != "start-node" &&
        graph.value.toJSON().cells.find(e => e.id == item.target.cell).shape != 'end-node'
    );


    let hop = [];
    lineArr.map(item => {
        hop.push({
            from: flowData.value[item.source.cell]?.name,
            to: flowData.value[item.target.cell]?.name,
            enabled: 'Y'
        })
    })

    let connection = arr.map(item => {
        return {
            name: item.connection,
        }
    }).filter(item => item.name) // 过滤掉name为undefined/null/空的情况

    let uniqueArray = Object.values(connection.reduce((acc, cur) => {
        if (cur.name) { // 增加有效性判断
            acc[cur.name] = cur; // 如果存在相同的name，则后一个会覆盖前一个，保证了唯一性
        }
        return acc;
    }, {}));

    let obj = {
        transformation: {
            ...(uniqueArray.length > 0 && { connection: uniqueArray.length === 1 ? uniqueArray[0] : uniqueArray }),
            // connection: uniqueArray.length == 1 ? uniqueArray[0] : uniqueArray,
            order: {
                hop: arr.length == 1 ?
                    {
                        "from": arr[0].name,
                        "to": arr[1]?.name,
                        "enabled": "Y"
                    } : hop//transformData(arr),
            },
            step: arr
        }
    }
    return obj;
}

let initData = () => {
    if (currentRoute.query.ktrTaskName)
    {
        intCount.setFlowName(currentRoute.query.ktrTaskName);
    }
    if (currentRoute.query.ktrJsonId) {
        selectById(currentRoute.query.ktrJsonId).then(res => {
            const rawData = Base64.decode(res.data.antvJsonContent);
            console.log('rawData',rawData)
            let obj = JSON.parse(rawData);
            console.log('obj',obj)
            flowCount.loadData(obj?.data); // 先加载数据到store
            graph.value.fromJSON(obj?.graph);  // 再渲染图形
            flowCount.setFlowData(obj?.data);
            intCount.setFlowName(res.data?.ktrJsonName || '');
        })
      // 更新ID计数器
      flowCount.updateIdCounterFromExistingData();
    } else {
        flowCount.resetIdCounter(); // 重置ID计数器
        flowCount.setFlowData({});
        intCount.setFlowName('');
    }
}


let toJSON = () => {
    // console.log(graph.value.toJSON())
    if (!flowName.value) {
        ElMessage.warning('请输入流程名称');
        return;
    }

    ElMessageBox.confirm('是否确认保存?', '提示', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(async () => {


        let fun = currentRoute.query.ktrJsonId ? updateByIdV2 : insertV2
        let obj = {
            antvJsonContent: Base64.encode(JSON.stringify({
                graph: graph.value.toJSON(),
                data: flowData.value
            })),
            ktrJsonContent:  Base64.encode(JSON.stringify(setData())),
            ktrJsonName: flowName.value,
            ...currentRoute.query
        }

        console.log(obj)
        let res = await fun(obj);
        ElMessage.success('保存成功');

    }).catch(() => {
    })


}

let toRun = () => {
    console.log('toRun')
    logVisible.value = true;
    flowCount.setLogDisposition(
        {
            graphJson: graph.value.toJSON(),
        }
    )
}


let toRunStart = async () => {
    logVisible.value = false;
    await nextTick()
    runVisible.value = true;

}

let toClose = () => {
    ElMessageBox.confirm('是否确认关闭页面?', '提示', {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(() => {
        flowCount.resetIdCounter(); // 重置id计数器
        window.close()
    }).catch(() => {
    })

}

const handleOpen = (index) => {
  console.log('展开菜单:', index);
};

const handleClose = (index) => {
  console.log('折叠菜单:', index);
};


</script>

<style scoped lang="scss">

.flow-container {
  display: flex;
  min-height: 500px;
  height: 100%;
  position: relative;

  #flow-stencil {
    width: 200px;
    height: auto;
    position: relative;
    border-right: 1px solid #dfe3e8;
    background-color: #fff;
  }

  #flow-main {
    flex: 1;
    height: 100% !important;
    background-color: #ebedf1;
  }

  #flow-toolbar {
    position: absolute;
    top: 0px;
    left: 200px;
    z-index: 10;
    width: calc(100% - 200px);
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 45px;
    background: #fff;
    box-shadow: 0px 4px 4px 0px #EBEDF1; //border-radius: 4px;
    .flow-toolbar-item {
      margin-right: 10px;
      mergin-left: 10px;
      padding: 10px;
    }
  }

  #flow-contextmenu {
    position: absolute;
    bottom: 0px;
    left: 200px;
    z-index: 10;
    width: calc(100% - 200px);
    height: 250px;
    background: #fff;
    border-radius: 4px;
  }

  .flow-stencil-title {
    position: relative;
    margin: 7px 0 7px 15px;
    color: #000;
    font-weight: 700;
    padding-left: 15px;
  }
  .flow-stencil-title::before {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    content: "";
    display: block;
    height: 15px;
    width: 5px;
    background: #3972f9;
  }

  .flow-stencil-content {
    color: #565758;
    height: 32px;
    line-height: 32px;
    //background: #f5f7fa;
    margin: 5px 0 5px 15px;
    border-radius: 5px;
    padding-left: 8px;
    overflow: hidden;
    width: calc(100% - 30px);
    display: flex;
    align-items: center;

    img {
      margin-right: 10px;
      margin-left: 10px;
    }

    &:hover {
      cursor: move;
      background: #f5f7fa;
      border: 1px solid #DCDFE6;
      border-radius: 2px;
      padding-left: 5px;
      background-color: rgba(2, 82, 217, 0.1);
    }
  }


}
.flow-stencil-menu {
  border-right: none !important;
  background: transparent !important;
}

.flow-stencil-menu .el-sub-menu__title,
.flow-stencil-menu .el-menu-item {
  height: 40px;
  line-height: 40px;
}

.flow-stencil-icon {
  width: 20px;
  vertical-align: middle;
  margin-right: 8px;
}

</style>